﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;

namespace Jemgine.Data
{
        [Jemgine.Engine.Category("Primitive"), Jemgine.Engine.Component]
    public class Bone : Data.Translatable, IVertex, ReferenceTarget
    {
        public Vector2 Position { get; set; }

        [ContentSerializerIgnore]
        public int G_ID = -1;

        [ContentSerializerIgnore]
        internal Skeleton Owner = null;

        [ContentSerializerIgnore]
        internal bool OrientationSetByController = false;
        [ContentSerializerIgnore]
        internal float ControllerAngle = 0.0f;

        public int Parent { get; set; }

        [ContentSerializerIgnore]
        public Matrix Transform = Matrix.Identity;

        public Bone() { Parent = -1;  }

        public Bone(Vector2 _o)
        {
            Position = _o;
            Parent = -1;
        }

        public object PrepareToTranslate()
        {
            return Position;
        }

        public void ApplyTranslation(object Tag, Vector2 MouseDelta)
        {
            Position = (Vector2)Tag + MouseDelta;
        }

        public void Snap(Data.Snapper Snapper)
        {
            Position = Snapper.SnapPosition(Position);
        }

        internal Bone DeepClone()
        {
            return new Bone { Position = Position, Parent = Parent };
        }

        int ReferenceTarget.ReferenceID { get; set; }
        
        Vector2 ReferenceTarget.GetWorldPosition()
        {
            if (Owner == null || Owner.Owner == null) return Vector2.Zero;
            return Owner.Owner.Position + Position;
        }
    }

    public class _BonePosition
    {
        public float Angle { get; set; }

        public _BonePosition() { Angle = 0; }
    }

    public class Animation
    {
        public String Name { get; set; }
        public List<int> Frames { get; set; }

        [ContentSerializer(Optional = true)]
        public float Rate { get; set; }

        public Animation()
        {
            Name = "Animation";
            Frames = new List<int>();
            Rate = 1.0f;
        }
    }

    public class Skeleton : EntityElement, EditorDrawable, ReferenceTarget, Translatable, HasVerticies
    {
        [ContentSerializer(Optional = true)]
        public int BoneID { get; set; }
        [ContentSerializerIgnore]
        public Bone BoneReference { get; set; }

        public List<Bone> Bones = new List<Bone>();
        public Jemgine.Engine.DynamicGrid<_BonePosition> KeyFrames = new Engine.DynamicGrid<_BonePosition>();
        public List<Animation> Animations = new List<Animation>();

        [ContentSerializerIgnore]
        public int EditorFrame = 0;

        public Skeleton()
        {
            Bones.Add(new Bone(Vector2.Zero));
            KeyFrames.AddColumn(0);
            KeyFrames.AddRow(0);
        }

        public void ClearAllIDs()
        {
            foreach (var Bone in Bones)
            {
                Bone.G_ID = -1;
                (Bone as ReferenceTarget).ReferenceID = -1;
            }
        }

        public int AssignGlobalBoneIDs(int NextID) { foreach (var Bone in Bones) { Bone.G_ID = NextID; ++NextID; } return NextID; }

        public void EnumerateBones(List<Bone> _bones) { _bones.AddRange(Bones); }

        public Skeleton DeepClone()
        {
            var R = new Skeleton();
            R.Bones = new List<Bone>();
            foreach (var Bone in Bones) R.Bones.Add(Bone.DeepClone());
            R.Animations = new List<Animation>(Animations);
            R.KeyFrames = KeyFrames;
            return R;
        }

        public bool PointInside(Vector2 Point)
        {
            Point -= Bones[0].Position;
            return Point.X > -1 && Point.X < 1 && Point.Y > -1 && Point.Y < 1;
        }

        public Engine.Selection.Types SelectionType()
        {
            return Engine.Selection.Types.EntityElement;
        }

        public EntityElement Clone()
        {
            return DeepClone();
        }

        [ContentSerializerIgnore]
        public EntityRecord Owner { get; private set; }

        public void SetOwner(EntityRecord E)
        {
            Owner = E;
            foreach (var Bone in Bones) Bone.Owner = this;
        }

        public void Initialize(EntityRecord Owner, ContentManager Content)
        {
        }

        public void Destroy()
        {
        }

        [ContentSerializerIgnore]
        public int ReferenceID { get; set; }

        public Vector2 GetWorldPosition()
        {
            if (Owner == null) return Bones[0].Position;
            else return Owner.Position + Bones[0].Position;
        }

        public object PrepareToTranslate()
        {
            return Bones[0].PrepareToTranslate();
        }

        public void ApplyTranslation(object Tag, Vector2 MouseDelta)
        {
            Bones[0].ApplyTranslation(Tag, MouseDelta);
        }

        public void Snap(Data.Snapper Snapper)
        {
            Bones[0].Snap(Snapper);
        }

        #region EditorDrawable Members

        public void Draw(int ID, EditorDrawData Context,
            Data.SelectionType Selected)
        {
            Context.Renderer.Color = EditorDrawData.DrawColor(Selected);
            Context.Renderer.DrawSkeleton(this);
        }

        #endregion

        public int KeyFrameCount { get { return KeyFrames.Height; } }

        public void AddBone(Bone bone)
        {
            bone.Owner = this;
            Bones.Add(bone);
            KeyFrames.AddColumn(KeyFrames.Width);
        }

        public void RemoveBone(int index)
        {
            Bones.RemoveAt(index);
            KeyFrames.RemoveColumn(index);
        }

        public Bone GetBone(int Index)
        {
            return Bones[Index];
        }

        public int Count
        {
            get { return Bones.Count; }
        }

        public IVertex GetVertex(int Index)
        {
            return Bones[Index] as IVertex;
        }

        public void ClearDecomposedCache()
        {
        }

        public void SetIdentityTransform()
        {
            foreach (var Bone in Bones)
                Bone.Transform = Matrix.Identity;
        }

        public float[] GetFrameAngles(int Keyframe)
        {
            var R = new float[Bones.Count];
            for (int L_ID = 0; L_ID < Bones.Count; ++L_ID)
                R[L_ID] = KeyFrames[L_ID, Keyframe].Angle;
            return R;
        }

        public static float[] BlendFrameAngles(float[] A, float[] B, float Percentage)
        {
            var R = new float[A.Length];
            for (int I = 0; I < A.Length; ++I)
            {

                float DeltaRot = Math.Angle.Delta(Math.Angle.Normalize(A[I]), Math.Angle.Normalize(B[I]));
                R[I] = Math.Angle.Normalize(Math.Angle.Normalize(A[I]) + (DeltaRot * Percentage));
            }
            return R;
        }

        public static float[] AddFrameAngles(float[] A, float[] B)
        {
            var R = new float[A.Length];
            for (int I = 0; I < A.Length; ++I)
                R[I] = Math.Angle.Normalize(A[I] + B[I]);
            return R;
        }

        public void SetTransform(float[] Angles)
        {
            for (int L_ID = 0; L_ID < Bones.Count; ++L_ID)
            {
                var Bone = Bones[L_ID];
                Bone Parent = Bone.Parent == -1 ? null : Bones[Bone.Parent];
                var ParentPosition = Parent == null ? Vector2.Zero : Parent.Position;
                var ParentTransform = Parent == null ? Matrix.Identity : Parent.Transform;

                ParentPosition = Vector2.Transform(ParentPosition, ParentTransform);
                float Rot = Bone.OrientationSetByController ? Bone.ControllerAngle : Angles[L_ID];
                Bone.Transform = ParentTransform *
                    Matrix.CreateTranslation(new Vector3(-ParentPosition, 0.0f)) *
                    Matrix.CreateRotationZ(Rot) *
                    Matrix.CreateTranslation(new Vector3(ParentPosition, 0.0f));
            }
        }
    }
}
